引子
需求: 多个课程表都需要关联到价格表
1. 设计方式一

2. 设计方式二

3. 设计方式三(最终的设计模式)

ContentType
1. 什么时候使用 ContentType
- 当一张表需要和多张表建立外键关系的时候
2. ContentType 的说明
- ContentType 的设计模式实际上就是引子中的设计方式三
- ContentType 所使用的表名表就是在执行 migrate 后自动创建 django_content_type 表(存放着都是表名,当有新的表被创建时,会自动添加表名数据)


3. ContentType 的使用
- 表的创建
- content_type、 object_id、 content_object 字段属性名是固定写法不能随意改变
# models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType # 导入 ContentType 表(即: django_content_type)
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
class VIPCourse(models.Model):
"""
VIP课程
"""
title = models.CharField(max_length=32, verbose_name='VIP课程名称')
coupon_policy = GenericRelation('Coupon') # 不会在数据库中生产字段,只用于反向查询数据,传入使用了 ContentType 的表名
class BasisCourse(models.Model):
"""
基础课程
"""
title = models.CharField(max_length=32, verbose_name='基础课程名称')
coupon_policy = GenericRelation('Coupon') # 不会在数据库中生产字段,只用于反向查询数据,传入使用了 ContentType 的表名
class Coupon(models.Model):
"""
优惠券
"""
content_type = models.ForeignKey(ContentType) # 关联表名表(即: django_content_type),content_type 是固定字段属性名,不能随意改变
object_id = models.PositiveIntegerField() # 正整数类型,存放需要关联的课程 id,object_id 是固定字段属性名,不能随意改变
content_object = GenericForeignKey('content_type', 'object_id') # 不会在数据库中生产字段,只用于添加数据和正向查询数据,content_object 是固定字段属性名,不能随意改变
title = models.CharField(max_length=32, verbose_name='优惠券名称')
3. 添加数据
- 方式一
from django.contrib.contenttypes.models import ContentType # 导入 ContentType 表(即: django_content_type)
from app01.models import *
Coupon.objects.create(
title='vip课程优惠券一',
content_type=ContentType.objects.filter(model='vipcourse').first(), # 查询到的ContentType表名数据对象
object_id=1 # 课程id
)
Coupon.objects.create(
title='基础课程优惠券一',
content_type=ContentType.objects.filter(model='basiscourse').first(), # 查询到的ContentType表名数据对象
object_id=2 # 课程id
)
- 方式二 -> 通过 GenericForeignKey 表类方法所创建 content_object 字段进行添加 -> 推荐使用
- 语法: content_object = 查询到的数据对象
- 原理: 因为可以通过查询的对象获取当前数据的id和表名,然后 ContentType 组件帮你进行添加
from django.contrib.contenttypes.models import ContentType # 导入 ContentType 表(即: django_content_type)
from app01.models import *
Coupon.objects.create(
title='vip课程优惠券一',
content_object=VIPCourse.objects.filter(pk=2).first() # 通过 GenericForeignKey 表类方法所创建 content_object 字段进行添加,传入查询到的数据对象
)
4. 正向查询
- 通过定义了相关ContentType字段属性表的数据,查询出与该条数据相关的表的数据
- 通过 GenericForeignKey 表类方法所创建 content_object 字段进行正向查询
# 通过优惠券表的数据查询出与该优惠券数据相关的课程数据
from app01.models import *
coupon_obj = Coupon.objects.filter(pk=5).first()
course_obj = coupon_obj.content_object # VIPCourse object
course_title = coupon_obj.content_object.title # Vue项目课程
5. 反向查询
- 通过数据对象查询出与该数据对象相关的所有定义了相关ContentType字段属性表的数据
- 通过 GenericRelation 表类方法所创建字段进行反向查询
# 通过课程数据查询出该课程的所有优惠券数据
from app01.models import *
vip_course_obj = VIPCourse.objects.filter(pk=2).first()
coupon_list = vip_course_obj.coupon_policy.all() # <QuerySet [<Coupon: Coupon object>, <Coupon: Coupon object>, <Coupon: Coupon object>]>
for coupon in coupon_list:
print(coupon.title) # vip课程优惠券一
ORM-F查询 →